import { changeTypeByTaskShape, typeConfig } from '../../../config';
import { jnpfConfigBpmnContextPad } from '../../../config/contextPad';
import {
  bpmnEnd,
  bpmnTask,
  typeTask,
  typeSubFlow,
  typeLabel,
  bpmnInclusive,
  typeConfluence,
  typeGateway,
  bpmnStart,
  typeTrigger,
  bpmnTrigger,
  typeExecute,
  bpmnLabel,
  typeStart,
} from '../../../config/variableName';
import { BPMNTreeBuilder } from '../../../utils/constructTreeUtil';
import { DEFAULT_DISTANCE } from '../../../config/constants';
import { buildBitUUID } from '../../../utils/uuidUtil';
import { NodeUtils } from '../../../utils/nodeUtil';

const CustomizeContextPad = (contextPadProvider: any, element: any) => {
  let type = element.type;
  let isAction = true;
  if (typeConfig[type]) {
    const {
      _autoPlace: autoPlace,
      _create: create,
      _elementFactory: elementFactory,
      _modeling: modeling,
      _connect: connects,
      _injector: injector,
      _rules: rules,
    } = contextPadProvider;
    let wnType = element.wnType;
    if (wnType === bpmnTrigger) type = bpmnTrigger;
    if (changeTypeByTaskShape[wnType]) type = changeTypeByTaskShape[wnType];
    const { contextPad, shapeType } = typeConfig[type];
    const { del } = jnpfConfigBpmnContextPad;
    let customization = contextPad?.customization;
    if (type === bpmnLabel) {
      let connect = element?.labelTarget;
      let target = connect.target;
      // 执行节点的线label使用groupCustomization
      if (changeTypeByTaskShape[target.wnType]) {
        customization = contextPad?.groupCustomization;
      }
    }
    if (type === shapeType) {
      if (contextPad) {
        if (contextPad.default) return defaultContextPad;
        else if (customization) {
          let result: any = {};
          for (let key of Object.keys(customization)) {
            let data = customization[key];
            if (data.group === 'model') {
              let options: any = {
                wnName: typeConfig[key]?.renderer.rendererName,
              };
              if (element.type === bpmnStart && key === typeGateway) {
                // 开始节点只有分类节点 因为网关的分流节点和合流节点类型一致 多增加一个字段来表示
                options = {
                  wnName: typeConfig[key]?.renderer.rendererName,
                  wnGatewayType: typeGateway,
                  wnType: typeGateway,
                  icon: data.icon,
                };
              }
              result[data.name] = appendAction(data.type, data.elementName, data.className, data.title, data.wnType, options);
            } else if (data.group === 'connect') {
              result[data.name] = {
                group: data.group,
                className: data.className,
                title: data.title,
                action: {
                  click: isAction && startConnect,
                },
              };
            } else if (data.group === 'edit') {
              result[data.name] = {
                group: data.group,
                className: data.className,
                title: data.title,
                action: {
                  click: removeElement,
                },
              };
            }
          }
          return Object.assign(result);
        } else return defaultContextPad();
      }
      // 单个节点删除功能
      function removeElement() {
        rules.allowed('elements.delete', { elements: [element] });
      }
      // 开始连线（拖拽）
      function startConnect(event: any, element: any) {
        connects.start(event, element);
      }
      // 添加事件
      function appendAction(type: any, name: any, className: any, title: any, wnType: any, options?: any) {
        const appendStart = (event: any, element: any) => {
          if (type === typeSubFlow) type = bpmnTask;
          let bpmnFactory = elementFactory._bpmnFactory;
          let businessObject = bpmnFactory.create(type);
          let shape = elementFactory.createShape(Object.assign({ type, name, wnType, ...options }, businessObject));
          create.start(event, shape, { source: element });
        };
        const autoPlaceAppend = async (_event: any, element: any) => {
          if (type === typeSubFlow || type === typeTrigger || changeTypeByTaskShape[wnType]) type = bpmnTask;
          let bpmnFactory = elementFactory._bpmnFactory;
          let businessObject = bpmnFactory.create(type);
          let shape = elementFactory.createShape(Object.assign({ type, name: name, businessObject, wnType: wnType }, options));
          if (element.type === typeLabel) {
            let businessTaskObject = bpmnFactory.create(bpmnTask);
            let businessTask2Object = bpmnFactory.create(bpmnTask);
            let businessInclusiveObject = bpmnFactory.create(bpmnInclusive);
            let shape: any;
            if (wnType === typeGateway) {
              let id = `Gateway_${buildBitUUID()}_isSimple`;
              businessObject.id = id;
              shape = elementFactory.createShape(Object.assign({ type, id: id, name: name, businessObject, wnType: wnType }, options));
            } else {
              shape = elementFactory.createShape(Object.assign({ type, name: name, businessObject, wnType: wnType }, options));
            }
            updateShapePosition(shape, element, businessTaskObject, businessInclusiveObject, businessTask2Object);
          } else {
            autoPlace.append(element, shape);
            // 自研
            if (element.wnType === typeGateway) {
              // 获取对应的合流网关
              const gateway = injector.get('elementRegistry').get(element.id + '_confluence');
              modeling.connect(shape, gateway);
            }
            // 处理执行节点的美化 (获取到最近的合流节点 判断分组与合流坐标距离计算是否需要进行偏移)
            if (changeTypeByTaskShape[shape.wnType]) {
              modeling.updateProperties(shape, {
                customGroupId: element.businessObject.$attrs.customGroupId,
              });
              let groupShape = injector.get('elementRegistry').get(element.businessObject.$attrs.customGroupId);
              let shapeRight = shape.x + shape.width;
              let shapeBottom = shape.y + shape.height;
              let groupRight = groupShape.x + groupShape.width;
              let groupBottom = groupShape.y + groupShape.height;
              var newBounds: any = {
                x: groupShape.x, // 保持 x 位置不变
                y: groupShape.y, // 保持 y 位置不变
                width: groupShape.width,
                height: groupShape.height,
              };
              if (shapeRight >= groupRight) newBounds.width = shapeRight + 25 - groupShape.x;
              if (shapeBottom >= groupBottom) newBounds.height = shapeBottom + 15 - groupShape.y;
              modeling.resizeShape(groupShape, newBounds);
              autoExecute(groupShape);
            }
          }
          // 计算需要横向偏移的元素(只有出现分流的情况下需要用到)
          if (element?.type === typeLabel || element.wnType === typeGateway || element.wnType === typeTrigger || changeTypeByTaskShape[element.wnType])
            onAutoPosition();
        };
        var append = autoPlace ? autoPlaceAppend : appendStart;
        let disable = isAction;
        if (element?.labelTarget?.source?.wnType === typeConfluence && (wnType === typeGateway || wnType === typeTrigger)) disable = false;
        if (element?.labelTarget?.source?.wnType === typeSubFlow && wnType === typeGateway) disable = false;
        if (element?.labelTarget?.target?.wnType === typeGateway && wnType === typeGateway) disable = false;
        if (
          element?.labelTarget?.target?.wnType === typeTrigger &&
          (wnType === typeSubFlow || wnType === typeExecute || wnType === typeGateway || wnType === typeTask)
        )
          if (changeTypeByTaskShape[element?.labelTarget?.target?.wnType] && wnType === typeGateway) disable = false;
        if ((element?.labelTarget?.source?.wnType === typeStart || element?.labelTarget?.source?.wnType === typeSubFlow) && wnType === typeTrigger)
          disable = false;
        return {
          group: 'model',
          className: className,
          title: title,
          disable: disable,
          action: { click: disable && append },
        };
      }
      // 默认contextPad
      function defaultContextPad() {
        return Object.assign({
          [del.name]: {
            group: del.group,
            className: del.className,
            title: del.title,
            action: {
              click: removeElement,
            },
          },
        });
      }
      // 判断元素与网关之间的垂直距离是否小于某个阈值
      function isWithinThreshold(target, source, threshold, processedElements: any) {
        // 这里假设网关在上方，即网关的 y 坐标小于当前元素的 y 坐标
        let gatewayY = target.y;
        let sourceElementY = source.y;
        let elementObj: any;
        let map = new Map(
          Array.from(processedElements, (item: any) => [item.id, item]), // 使用 id 作为键
        );
        // 如果当前元素是合流网关 获取分流到合流内所有元素 并且获取y最大值的元素与 当前分流坐标做对比
        if (target.wnType === typeConfluence) {
          let allElements = injector.get('elementRegistry').getAll();
          let treeBuilder = new BPMNTreeBuilder(allElements);
          let gatewayElement = injector.get('elementRegistry').get(target.id.replace('_confluence', ''));
          elementObj = treeBuilder.onComputerMaxElementH(injector, target, gatewayElement, [], map, threshold);
        }
        if (map.has(source.id)) {
          sourceElementY = sourceElementY + threshold;
        }
        return gatewayY - sourceElementY > threshold && gatewayY > sourceElementY;
      }
      function moveConnectedElements(connection: any, height: any, useWithinThreshold: boolean = true) {
        const stack: any = []; // 用于存储待处理的连接线
        const processedElements = new Set(); // 记录已经处理过的目标元素
        stack.push(connection); // 从给定的连接线开始
        while (stack.length > 0) {
          const currentConnection: any = stack.pop();
          const target = currentConnection.target;
          if (!target) continue; // 如果没有目标元素，跳过
          if (processedElements.has(target)) continue; // 如果目标元素已经处理过，跳过
          if (useWithinThreshold && isWithinThreshold(target, currentConnection.source, height, processedElements)) continue;
          processedElements.add(target); // 标记该元素已经被处理
          // 遍历目标元素的所有出线连接，并将它们压入栈
          const outgoingConnections: any = target.outgoing || [];
          for (const outgoingConnection of outgoingConnections) {
            stack.push(outgoingConnection); // 将所有关联的连接线压入栈中
          }
        }
        return Array.from(processedElements);
      }
      function autoExecute(groupShape: any) {
        // 判断groupBottom 和该触发节点对应的任务节点元素与合流节点间的距离
        // 1. 获取到触发节点对应的任务节点 后续获取到该元素在哪个合流元素内部 获取到合流网关 判断合流网关和当前的判断groupBottom坐标 如果小于则需要对合流后的所有元素进行统一偏移
        let triggerSourceShape: any;
        injector.get('elementRegistry').forEach(e => {
          if (e.businessObject.$attrs.customGroupId === groupShape.id && e.wnType === typeTrigger) triggerSourceShape = e.incoming[0].source;
        });
        // 迭代获取最近的合流节点 如果下一个节点时分流节点 则获取到对应的合流节点继续获取下一个节点
        function getConfluence(shape: any) {
          let targetList = NodeUtils.getNextElementList(shape, injector.get('elementRegistry').getAll());
          let confluence: any;
          targetList.map((element: any) => {
            if (element.wnType === typeConfluence) {
              confluence = element;
              return;
            }
            if (element.wnType != typeTrigger) confluence = getConfluence(element);
            if (element.wnType === typeGateway) confluence = getConfluence(injector.get('elementRegistry').get(element.id + '_confluence'));
          });
          return confluence;
        }
        let confluenceShape = getConfluence(triggerSourceShape);
        if (confluenceShape) {
          if (groupShape && groupShape.y + groupShape.height + 50 >= confluenceShape.y) {
            modeling.moveElements(moveConnectedElements(confluenceShape.incoming[0], 0, false), {
              x: 0,
              y: groupShape.y + groupShape.height + DEFAULT_DISTANCE - 15 - confluenceShape.y,
            });
          }
        }
      }
      async function updateShapePosition(shape: any, element: any, businessTaskObject, businessInclusiveObject, businessTask2Object) {
        let connectElement: any = element?.labelTarget;
        let targetElement = connectElement?.target;
        let sourceElement = connectElement?.source;
        let promises: any = [];
        if (shape.wnType === typeTrigger) {
          // 生成一个触发节点
          let labelTargetY = (DEFAULT_DISTANCE + typeConfig[bpmnTask].renderer.attr.height) * 2;
          let y = labelTargetY - (targetElement.y - sourceElement.y);
          if (targetElement.wnType === typeConfluence) {
            modeling.moveElements(moveConnectedElements(element.labelTarget, labelTargetY), {
              x: 0,
              y: y,
            });
          }
          autoPlace.append(sourceElement, shape);
          let groupShapes = modeling.createShape(
            {
              type: 'bpmn:Group',
            },
            { x: shape.x - 25, y: shape.y - 15, width: 250, height: 118 },
            shape.parent,
          );
          modeling.updateProperties(shape, {
            customGroupId: groupShapes.id,
          });
        } else if (shape.wnType === typeGateway) {
          // 生成两个分支 包含分支 → 条件节点*2 → 任务节点*2 → 合流节点*1 最后融入到旧元素内
          let shape1 = elementFactory.createShape(
            Object.assign({ type: bpmnTask, name: typeTask, businessObject: businessTaskObject, wnType: typeTask }, { wnName: '审批节点' }),
          );
          businessInclusiveObject.id = shape.id + '_confluence';
          let gateway = elementFactory.createShape(
            Object.assign(
              {
                id: shape.id + '_confluence',
                type: bpmnInclusive,
                name: typeConfluence,
                businessObject: businessInclusiveObject,
                wnType: typeConfluence,
              },
              { wnName: '合流' },
            ),
          );
          let shape2 = elementFactory.createShape(
            Object.assign({ type: bpmnTask, name: typeTask, businessObject: businessTask2Object, wnType: typeTask }, { wnName: '审批节点' }),
          );
          let labelTargetY =
            DEFAULT_DISTANCE * 4 +
            typeConfig[bpmnTask].renderer.attr.height +
            typeConfig[sourceElement.type].renderer.attr.height +
            typeConfig[bpmnInclusive].renderer.attr.height;
          promises.push(
            modeling.moveElements(moveConnectedElements(element.labelTarget, labelTargetY), {
              x: 0,
              y: labelTargetY - (targetElement.y - sourceElement.y),
            }),
          );
          promises.push(modeling.removeConnection(connectElement));
          promises.push(autoPlace.append(sourceElement, shape));
          promises.push(autoPlace.append(shape, shape1));
          promises.push(autoPlace.append(shape, shape2));
          promises.push(autoPlace.append(shape1, gateway));
          promises.push(modeling.connect(shape2, gateway));
          promises.push(modeling.connect(gateway, targetElement));
        } else if (targetElement) {
          let labelTargetY = (DEFAULT_DISTANCE + typeConfig[bpmnTask].renderer.attr.height) * 2;
          let y = labelTargetY - (targetElement.y - sourceElement.y);
          if (targetElement.type === bpmnEnd) y = DEFAULT_DISTANCE + typeConfig[bpmnTask].renderer.attr.height;
          if (sourceElement.type === bpmnStart) y = DEFAULT_DISTANCE + typeConfig[bpmnTask].renderer.attr.height;
          if (sourceElement.wnType === typeConfluence) {
            y = DEFAULT_DISTANCE + typeConfig[bpmnTask].renderer.attr.height;
            if (targetElement.y - sourceElement.y - DEFAULT_DISTANCE * 2 - typeConfig[bpmnTask].renderer.attr.height > 0) y = 0;
            if (targetElement.y - sourceElement.y - DEFAULT_DISTANCE * 2 - typeConfig[bpmnTask].renderer.attr.height < 0)
              y = Math.abs(targetElement.y - sourceElement.y - DEFAULT_DISTANCE * 2 - typeConfig[bpmnTask].renderer.attr.height);
          }
          if (sourceElement?.wnType === typeConfluence && targetElement?.wnType === typeConfluence) {
            let maxY = -Infinity;
            let id;
            targetElement = injector.get('elementRegistry').get(targetElement.id);
            targetElement.incoming?.length > 0 &&
              targetElement.incoming.map(item => {
                let currentElement: any = injector.get('elementRegistry').get(item.id);
                if (currentElement.source?.y > maxY) {
                  id = currentElement.source.id;
                  maxY = currentElement.source.y;
                }
              });
            if (targetElement.y - maxY <= DEFAULT_DISTANCE && sourceElement.id != id) {
              y = DEFAULT_DISTANCE - (targetElement.y - maxY);
            }
          }
          if (changeTypeByTaskShape[shape.wnType]) {
            let source = contextPadProvider._injector.get('elementRegistry').get(connectElement.id).source;
            // 需要增加customGroupId
            modeling.updateProperties(shape, {
              customGroupId: source.businessObject.$attrs.customGroupId,
            });
          }
          promises.push(
            modeling.moveElements(moveConnectedElements(element.labelTarget, labelTargetY), {
              x: 0,
              y: y,
            }),
          );
          promises.push(modeling.removeConnection(connectElement));
          autoPlace.append(sourceElement, shape);
          promises.push(modeling.connect(shape, targetElement));
        }
        Promise.all(promises);
        let elementRegistry = injector.get('elementRegistry');
        let treeBuilder = new BPMNTreeBuilder(elementRegistry.getAll()); // 实例化工具类
        treeBuilder.resizeGroupShape(elementRegistry.getAll(), injector);
        if (changeTypeByTaskShape[shape.wnType]) {
          let groupShape = injector.get('elementRegistry').get(targetElement.businessObject.$attrs.customGroupId);
          autoExecute(groupShape);
        }
      }
      async function onAutoPosition() {
        let elementRegistry: any = await contextPadProvider._injector.get('elementRegistry');
        let allElements = elementRegistry.getAll();
        let treeBuilder = new BPMNTreeBuilder(allElements); // 实例化工具类
        let bpmnTree = treeBuilder.constructTree(1); // 构建树状数据结构
        let visited: any = new Map(); // 用于防止重复访问
        let shapeList: any = []; // 修改触发节点旁的连接线坐标
        let confluenceMap: any = new Map();
        treeBuilder.calculateVirtualWidth(bpmnTree, elementRegistry); // 计算虚拟宽度
        treeBuilder.traverseTreeBFS(bpmnTree, node => {
          node?.offset && node.x != node.offset.x && visited.set(node.id, node);
          if (node?.children?.length > 0) {
            let hasTrigger = node.children.some(o => o.wnType === typeTrigger);
            let hasConfluence = node.children.some(o => o.wnType === typeConfluence);
            let shape = elementRegistry.get(node.id);
            let confluence: any;
            if (shape.outgoing?.length) {
              shape.outgoing.map((connect: any) => {
                if (connect.target.wnType === typeTrigger) hasTrigger = true;
                if (connect.target.wnType === typeConfluence) {
                  confluence = connect.target;
                  hasConfluence = true;
                }
              });
            }
            if (hasTrigger && hasConfluence)
              shapeList.push({ shape: elementRegistry.get(node.id), treeShape: node, treeConfluence: node.children[0], confluence: confluence });
            if (node.wnType === typeConfluence) confluenceMap.set(node.id, node);
          }
        });
        treeBuilder.formatCanvas(Array.from(visited.values()), modeling, elementRegistry);
        // 如果某个节点的出线包含合流及触发节点元素 则将连接合流节点的线进行进行重新绘制（根据当前节点的虚拟宽度计算出偏移的xy轴坐标中点）
        // 虚拟宽度的最左侧坐标为 当前元素的x + width/2 - 虚拟宽度/2
        shapeList.map(({ shape, treeShape, treeConfluence, confluence }) => {
          let confluenceElement = confluenceMap.get(treeConfluence.id);
          let x = shape.x + shape.width / 2 - treeShape.virtualWidth / 2 + shape.width / 2;
          let newWaypoints: any = [];
          if (!confluenceElement) {
            confluenceElement = confluenceMap.get(confluence.id);
            let childrenShape = treeShape.children[treeShape.children.length - 1];
            if (childrenShape.wnType === typeConfluence) {
              childrenShape = treeShape.children[treeShape.children.length - 2];
            }
            x = (childrenShape.offset?.x ? childrenShape.offset.x : childrenShape.x) + childrenShape.virtualWidth / 2 + childrenShape.width / 2 + 120;
            newWaypoints = [
              { x: shape.x + shape.width, y: shape.y + shape.height / 2 },
              { x: x, y: shape.y + shape.height / 2 },
              { x: x, y: confluenceElement.y },
              { x: confluenceElement.x, y: confluenceElement.y },
            ];
          } else {
            newWaypoints = [
              { x: shape.x, y: shape.y + shape.height / 2 },
              { x: x, y: shape.y + shape.height / 2 },
              { x: x, y: confluenceElement.y },
              { x: confluenceElement.x, y: confluenceElement.y },
            ];
          }
          let connect = shape.outgoing[0];
          if (shape.outgoing?.length) {
            connect = shape.outgoing.find(connect => connect.target.wnType != typeTrigger);
          }
          modeling.updateWaypoints(connect, newWaypoints);
        });
        treeBuilder.resizeGroupShape(elementRegistry.getAll(), injector);
      }
    }

    return undefined;
  }
  return undefined;
};
export default CustomizeContextPad;
